package com.itfreer.file.remote.download;

import com.itfreer.file.FileSegmentInfo;
import com.itfreer.file.path.FileUpload;
import com.itfreer.file.remote.FileService;
import com.itfreer.file.remote.FileProgressListener;
import com.itfreer.utils.collection.ArrayDeque;
import com.itfreer.utils.collection.Queue;

import java.util.ArrayList;
import java.util.List;

/**
 * 定义文件下载器
 */
public class FileDownloader {

    // 需要下载的文件
	private String bucketName;
    // 需要下载的文件
    private String objectKey;
    // 文件块大小，100kb
    private Integer segmentSize = 1024000;
    // 文件下载队列
    private final Queue<Long> downQueue;
    // 当前的进程数
    private final List<Long> threadList = new ArrayList<>();
    private int threads = 3;

    public FileDownloader(String bucketName, String objectKey){
        this.bucketName = bucketName;
        this.objectKey = objectKey;
        this.downQueue = new ArrayDeque<Long>();
    }

    /**
     * 同步文件下载
     */
    public Boolean download(){
        // 先下载第一块
        FileSegmentInfo segmentInfo = new FileSegmentInfo();
        segmentInfo.setSegmentIndex(0L);
        segmentInfo.setSegmentSize(segmentSize);

        FileSegmentInfo fInfo = FileService.instance.downloadFile(bucketName, objectKey, segmentInfo);
        if(fInfo==null){
            throw new RuntimeException("文件下载失败！");
        }else{
            // 保存第一个文件块
            FileUpload.instance.uploadFile(bucketName, objectKey, fInfo);
            // 将后续文件块存储到队列中
            for(long i=1;i<fInfo.getSegmentCount();i++){
                downQueue.add(i);
            }
            // 分块下载
            blackDown();
            return true;
        }
    }

    /**
     * 异步文件下载
     */
    public void download(final FileProgressListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                first(listener);
            }
        }).start();
    }

    /**
     * 第一块下载
     */
    private void first(final FileProgressListener listener){
        // 先下载第一块
        FileSegmentInfo segmentInfo = new FileSegmentInfo();
        segmentInfo.setSegmentIndex(0L);
        segmentInfo.setSegmentSize(segmentSize);

        FileSegmentInfo fInfo = FileService.instance.downloadFile(bucketName, objectKey, segmentInfo);
        if(fInfo==null){
            if(listener!=null){
                listener.onFail("文件下载失败！");
            }
        }else{
            // 保存第一个文件块
            FileUpload.instance.uploadFile(bucketName, objectKey, fInfo);
            // 将后续文件块存储到队列中
            for(long i=1;i<fInfo.getSegmentCount();i++){
                downQueue.add(i);
            }
            // 多线程下载
            threadDown(listener);
        }
    }

    /**
     * 多线程下载
     */
    private void threadDown(final FileProgressListener listener) {
        Long black = downQueue.poll();
        while (black!=null){
            if(threadList.size() >= threads){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                FileSegmentInfo segmentInfo = new FileSegmentInfo();
                segmentInfo.setSegmentIndex(black);
                segmentInfo.setSegmentSize(segmentSize);
                DownloadThread downThread = new DownloadThread(bucketName, objectKey,
                        segmentInfo, downQueue, threadList, listener);
                downThread.setPriority(7);
                downThread.start();
                threadList.add(downThread.getId());
                black = downQueue.poll();
            }
        }
    }

    /**
     * 多线程下载
     */
    private void blackDown() {
        Long black = downQueue.poll();
        while (black!=null){
            FileSegmentInfo segmentInfo = new FileSegmentInfo();
            segmentInfo.setSegmentIndex(black);
            segmentInfo.setSegmentSize(segmentSize);

            FileSegmentInfo fInfo = FileService.instance.downloadFile(bucketName, objectKey, segmentInfo);
            if(fInfo==null){
                // 失败，则退回到队列重新下载
                downQueue.offer(segmentInfo.getSegmentIndex());
            }else{
                // 成功，则保存文件
                FileUpload.instance.uploadFile(bucketName, objectKey, fInfo);
            }

            black = downQueue.poll();
        }
    }
}
